
#include "zen_metal.id.h"
#include "zen_metal_type.h"
#include "zen_metal_render.h"

namespace Zen { namespace Metal {

	Buffer::Buffer()
	{
		m_id = std::shared_ptr<BufferID>(new BufferID);
	}

	void Buffer::create(size_t len, void const * data)
	{
		auto device = Device::Get()->getID()->device;
		if(data)
		{
			m_id->data = [device newBufferWithBytes:data length:len options:MTLResourceStorageModeShared];
		}
		else
		{
			m_id->data = [device newBufferWithLength:len options:MTLResourceStorageModeShared];
		}
	}

	void Buffer::update(size_t offset, size_t size, void const * data)
	{
		auto dst = (char*)m_id->data.contents + offset;
		::memcpy(dst, data, size);
	}

	std::shared_ptr<BufferID> Buffer::getID() const
	{
		return m_id;
	}

	ShaderKit::ShaderKit(std::string const & name)
	{
		m_name = name;
		m_id = std::shared_ptr<ShaderKitID>(new ShaderKitID);
	}


	void ShaderKit::create
	(std::string const & vshader,
	 std::string const & fshader)
	{
		m_id->create
		([NSString stringWithUTF8String:vshader.data()],
		 [NSString stringWithUTF8String:fshader.data()]);
	}

	void ShaderKit::create
	(std::string const & vshader,
	 std::string const & fshader,
	 Blends blends)
	{
		if(blends.pair.src == eBF::None || blends.pair.dst == eBF::None)
		{
			return this->create(vshader, fshader);
		}

		m_id->create
		([NSString stringWithUTF8String:vshader.data()],
		 [NSString stringWithUTF8String:fshader.data()],
		 (MTLBlendFactor)blends.pair.src, (MTLBlendFactor)blends.pair.dst,
		 (MTLBlendFactor)blends.pair.src_alpha,
		 (MTLBlendFactor)blends.pair.dst_alpha
		 );
	}
}}


namespace Zen { namespace Metal {

	void ShaderKitID::create
	(NSString * vertex_func_name,
	 NSString * fragment_func_name)
	{
		auto device_id = Device::Get()->getID();

		id <MTLFunction> vertexFunction = [device_id->library newFunctionWithName:vertex_func_name];

		id <MTLFunction> fragmentFunction = [device_id->library newFunctionWithName:fragment_func_name];

		MTLRenderPipelineDescriptor *desc = [[MTLRenderPipelineDescriptor alloc] init];
		desc.vertexFunction = vertexFunction;
		desc.fragmentFunction = fragmentFunction;
		desc.depthAttachmentPixelFormat = device_id->view.depthStencilPixelFormat;
		desc.stencilAttachmentPixelFormat = device_id->view.depthStencilPixelFormat;

		auto ca = desc.colorAttachments[0];
		ca.blendingEnabled = NO;
		ca.pixelFormat = device_id->view.colorPixelFormat;

		state = [device_id->device newRenderPipelineStateWithDescriptor:desc error:nil];
	}

	void ShaderKitID::create
	(NSString * vertex_func_name,
	 NSString * fragment_func_name,
	 MTLBlendFactor srcC, MTLBlendFactor dstC,
	 MTLBlendFactor srcA, MTLBlendFactor dstA)
	{
		auto device_id = Device::Get()->getID();

		id <MTLFunction> vertexFunction = [device_id->library newFunctionWithName:vertex_func_name];

		id <MTLFunction> fragmentFunction = [device_id->library newFunctionWithName:fragment_func_name];

		MTLRenderPipelineDescriptor *desc = [[MTLRenderPipelineDescriptor alloc] init];
		desc.vertexFunction = vertexFunction;
		desc.fragmentFunction = fragmentFunction;
		desc.depthAttachmentPixelFormat = device_id->view.depthStencilPixelFormat;
		desc.stencilAttachmentPixelFormat = device_id->view.depthStencilPixelFormat;

		auto ca = desc.colorAttachments[0];
		ca.blendingEnabled = YES;
		ca.pixelFormat = device_id->view.colorPixelFormat;
		ca.sourceRGBBlendFactor = srcC;
		ca.destinationRGBBlendFactor = dstC;
		ca.sourceAlphaBlendFactor = srcA;
		ca.destinationAlphaBlendFactor = dstA;
		ca.writeMask = MTLColorWriteMaskAll;

		state = [device_id->device newRenderPipelineStateWithDescriptor:desc error:nil];
	}

}}
